//
// Created by wangwanlong on 2024/11/1.
//

#ifndef MKAHYPAR_FPGA_GRAPH_H
#define MKAHYPAR_FPGA_GRAPH_H

namespace mkahypar {
  namespace ds {
    class NodeResource {
    public:
      NodeResource() : _weight(8) {}

      NodeResource(int sz) : _weight(sz) {}


      NodeResource(NodeResource &r) = default;

      NodeResource(const NodeResource &r) = default;

//      NodeResource(NodeResource &&r) = default;

      ~NodeResource() = default;

      int32_t &operator[](const int id) {
        return _weight[id];
      }

      void clear(){
        for (int i = 0; i < _weight.size(); i++) {
          _weight[i] = 0;
        }
      }

      NodeResource &operator/=(const int32_t &rhs) {
        if (rhs != 0) {
          for (int i = 0; i < _weight.size(); i++) {
            _weight[i] /= rhs;
          }
        }
        return *this;
      }
      bool operator==(NodeResource &rhs) {
        return _weight.size() == rhs._weight.size() &&
               std::is_permutation(_weight.begin(),
                                   _weight.end(),
                                   rhs._weight.begin());
      }

      NodeResource &operator+=(const NodeResource &rhs) {
        if (this != &rhs) {
          for (int i = 0; i < 8; i++)
            _weight[i] += rhs._weight[i];
        }
        return *this;
      }

      NodeResource &operator-=(const NodeResource &rhs) {
        if (this != &rhs) {
          for (int i = 0; i < rhs._weight.size(); i++)
            _weight[i] -= rhs._weight[i];

        }
        return *this;
      }

      NodeResource operator+(const NodeResource &rhs) const {
        NodeResource result = *this;
        result += rhs;
        return result;
      }

      NodeResource operator-(const NodeResource &rhs) const {
        NodeResource result = *this;
        result -= rhs;
        return result;
      }

      bool operator<(const NodeResource &rhs) const {
        for (int i = 0; i < _weight.size(); i++) {
          if (_weight[i] >= rhs._weight[i])
            return false;
        }
        return true;
      }


      size_t size() const {
        return _weight.size();
      }

      bool operator<=(const NodeResource &rhs) const {
        for (int i = 0; i < _weight.size(); i++) {
          if (_weight[i] > rhs._weight[i])
            return false;
        }
        return true;
      }

      void print() {
        for (int i = 0; i < _weight.size(); i++) {
          LLOG<< "(" << i + 1 << "): " << _weight[i] << " ";
        }
        LOG << "";
      }


      std::vector<int32_t> _weight;//节点资源
    };


    class Block{
    public:
      Block(int32_t lb,NodeResource& res):_bound_line(lb),
      _used_line(0),_bound_res(res){

      }

      //计算占用资源百分比最大的资源
      std::pair<double, int> getResUsedPercent(){
        double max_percent = -1;
        int max_type = -1;
        for (int type = 0;type < _used_res.size();++type) {
          if(_bound_res[type] == 0)
            continue;
          double percent = (double) _used_res[type] / (double) _bound_res[type];
          if (percent > max_percent) {
            max_percent = percent;
            max_type = type;
          }
        }
        return std::make_pair(max_percent, max_type);
      }

      int32_t _bound_line;//互联量上限
      int32_t _used_line;//使用的互联量
      NodeResource _used_res;//已使用的资源
      NodeResource _bound_res;//资源上限
      std::vector<int32_t>_adj_list;//相邻的block
    };
    class FPGAGraph {
      static constexpr int32_t kInvalidDistance = 10000000;//防止溢出
    public:
      FPGAGraph() : _fpga_num(0) {

      }


      void initialize() {
        _distance.resize(_fpga_num, std::vector<int32_t>(_fpga_num, kInvalidDistance));
      }

      void createNode(size_t line_bound, NodeResource &res) {
        ++_fpga_num;
        _fpga.emplace_back(line_bound,res);
      }

      void createEdge(int u, int v) {
        incidentNets(u).emplace_back(v);
        incidentNets(v).emplace_back(u);

      }

      std::vector<int32_t> &incidentNets(int &id) {
        return _fpga[id]._adj_list;
      }

      //计算所有fpga之间的距离--floyd
      void calDistance() {
        for (int i = 0; i < _fpga_num; ++i) {
          _distance[i][i] = 0;
          for (auto adj: incidentNets(i))
            _distance[i][adj] = 1;
        }
        for (int i = 0; i < _fpga_num; ++i) {
          for (int j = 0; j < _fpga_num; ++j) {
            for (int k = 0; k < _fpga_num; ++k) {
              if(j != k)
                _distance[j][k] = std::min(_distance[j][k],_distance[j][i] + _distance[i][k]);
            }
          }
        }
        //打印最短路
        for(int i=0;i<_fpga_num;++i){
          for(int j=0;j<_fpga_num;++j)
            std::cout<<_distance[i][j]<<" ";
          LOG<<"";
        }
      }

      void prinInfo() {
        //打印每个block的使用情况
        auto &num_partitions = _fpga_num;
        std::vector<double> percents(num_partitions, 0);
        for (int i = 0; i < num_partitions; i++) {
          LOG << "block id: " << i;
          LOG << "line used:" << _fpga[i]._used_line;
          LOG << "line bound:" << _fpga[i]._bound_line;
          std::cout << "res used:";
          _fpga[i]._used_res.print();
          std::cout << "res bound:";
          _fpga[i]._bound_res.print();
          std::cout << "res remain:";
          (_fpga[i]._bound_res - _fpga[i]._used_res).print();

          // 找到资源占用率最高的一个资源，作为使用百分比
          std::pair<double, int> max_percent_type = _fpga[i].getResUsedPercent();
          LOG << "the percent:" << max_percent_type.first * 100 << " the type:" << max_percent_type.second;
          percents[i] = max_percent_type.first;
        }

      }


      void reset(){
        for(int i=0;i<_fpga_num;++i){
          _fpga[i]._used_res.clear();
          _fpga[i]._used_line = 0;
        }
      }


      std::vector<std::vector<int32_t>> _distance;
      std::vector<Block>_fpga;
      int _fpga_num;
      int hop_limit;//最大允许的条数
    };
  }
}
#endif //MKAHYPAR_FPGA_GRAPH_H
